#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/mod_devicetable.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/wait.h>

struct key_info{
	char * name; //按键名称(人类看)
	int code;  //哪个按键(设备看)
	int value; //按键的值
	int flags; //按键的触发方式
	struct gpio_desc * gpio;  //GPIO号
};

struct key_report{
	int code;
	int value;
};

struct stm32mp157_key{
	struct key_report event;
	struct fasync_struct * fasync;
};

struct key_info key_list[] = {
	[0] = {
		.name = "stm32mp157_key1",
		.code = KEY_A,
		.flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
	},
	[1] = {
		.name = "stm32mp157_key2",
		.code = KEY_B,
		.flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
	},
	[2] = {
		.name = "stm32mp157_key3",
		.code = KEY_C,
		.flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
	},
};


static struct stm32mp157_key * key;

irqreturn_t key_drv_irq_handler(int irq_no, void * data)
{
	struct key_info * info = (struct key_info *)data;
	//按下
	if(gpiod_get_value(info->gpio) == 0) 
		info->value = 0;
	//松开
	else
		info->value = 1;
	key->event.code = info->code;
	key->event.value = info->value;
	kill_fasync(&key->fasync, SIGIO, POLL_IN);
	return IRQ_HANDLED;
}

ssize_t key_drv_read(struct file * filp, char __user * buff, size_t size, loff_t * pos)
{
	int ret;
	ret = copy_to_user(buff, &key->event, size);
	if(ret > 0)
	{
		printk("copy_to_user error\n");
		return -EAGAIN;
	}
	return 0;
}

int key_drv_fasync(int fd, struct file * filp, int on)
{
	return fasync_helper(fd, filp, on, &key->fasync);
}


const struct file_operations key_fops = {
	.read = key_drv_read,
	.fasync = key_drv_fasync,
};

struct miscdevice key_misc = {
	.minor = 199,
	.name = "key_drv",
	.fops = &key_fops,
};

int key_drv_probe(struct platform_device * pdev)
{
	int ret;
	int i;
	struct gpio_desc * gpio;

	//1. 创建全局的结构体对象
	key = kzalloc(sizeof(struct stm32mp157_key), GFP_KERNEL);
	if(key == NULL)
	{
		printk("kzalloc error\n");
		return -ENOMEM;
	}

	//2. 注册杂项设备
	ret = misc_register(&key_misc);
	if(ret < 0)
	{
		printk("misc_register error\n");
		goto kzalloc_err;
	}

	//3. 硬件初始化(注册中断)
	for(i = 0; i < ARRAY_SIZE(key_list); i++)
	{
		//获取设备数中描述的中断的软件中断号
		gpio = devm_gpiod_get_index(&pdev->dev, "key", i, GPIOD_ASIS);
		if(IS_ERR(gpio))
		{
			ret = PTR_ERR(gpio);
			printk("gpiod_get_index error\n");
			goto misc_register_err;
		}
		key_list[i].gpio = gpio;
		//注册中断
		//参数1: 软件中断号
		//参数2: 中断处理函数
		//参数3: 中断的触发方式
		//参数4: 中断描述(自定义)
		//参数5: 传递给中断处理函数的参数
		
		ret = request_irq(gpiod_to_irq(key_list[i].gpio), key_drv_irq_handler, key_list[i].flags, key_list[i].name, &key_list[i]);
		if(ret < 0)
		{
			printk("request_irq error\n");
			goto misc_register_err;
		}
	}
	return 0;

	misc_register_err:
		for(;i>0;i--)
		{
			free_irq(gpiod_to_irq(key_list[i-1].gpio), &key_list[i-1]);
		}
		misc_deregister(&key_misc);
	kzalloc_err:
		kfree(key);
	return ret;
}

int key_drv_remove(struct platform_device * pdev)
{
	int i;
	for(i = 0; i < ARRAY_SIZE(key_list); i++)
	{
		free_irq(gpiod_to_irq(key_list[i].gpio), &key_list[i]);
	}
	misc_deregister(&key_misc);
	kfree(key);
	return 0;
}

const struct of_device_id key_table[] = {
	{ .compatible = "stm32mp157_key_test3", },
	{}
};

struct platform_driver key_drv = {
	.probe = key_drv_probe,
	.remove = key_drv_remove,
	.driver = {
		.name = "stm32mp157_key",
		.of_match_table = key_table,
	},
};

static int __init key_drv_init(void)
{
	return platform_driver_register(&key_drv);
}

static void __exit key_drv_exit(void)
{
	platform_driver_unregister(&key_drv);
}

module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");

